import {
  createContext,
  useContext,
  useState,
  useCallback,
  Context,
  useEffect,
} from "react";
import * as Auth from "./authApi";
import useMount from "../hooks/useMount";
import { AuthInfo } from "./authApi";
import {
  NotificationError,
  NotificationSuccess,
} from "../components/common/Notification";

interface AuthContextProps {
  auth: AuthInfo;
  login: typeof Auth.login;
  register: typeof Auth.register;
  signOut: () => void;
  updateUsername: typeof Auth.updateUsername;
  updatePassword: typeof Auth.updatePassword;
  // 上面de写法 等同于下面这个分开写的类型
  verificationMailCode: (
    params: Parameters<typeof Auth.verificationMailCode>[number]
  ) => ReturnType<typeof Auth.verificationMailCode>;
}
const jsonAuth = Auth.getLocalUser_Info();
const localAuth = jsonAuth ? JSON.parse(jsonAuth) : undefined;

const MyContext: Context<AuthContextProps> = createContext(
  {} as AuthContextProps
);

const AuthContext = (props: any): JSX.Element => {
  const [auth, setAuth] = useState(localAuth);

  useEffect(() => {
    auth && Auth.setLocalUser_Info(auth);
  }, [auth]);

  const clearAuth = useCallback(() => {
    setAuth({});
  }, []);

  const signOut = useCallback(() => {
    Auth.clearLocalJWT();
    Auth.clearLocalUser_Info();
    clearAuth();
  }, [clearAuth]);

  const initAuth = useCallback(async () => {
    //用本地jwt去服务端验证 若成功则和localStorage的比较 不同则替换 失败则执行退出操作
    const result = await Auth.getAuthByToken();
    if (!result) return;
    const { status, user, message } = result;
    // console.log("init auth:", user);
    if (status) setAuth(user);
    else {
      signOut();
      NotificationError({ message });
    }
  }, [signOut]);

  useMount(() => {
    initAuth();
  });

  const handleRequestSetAuth: <
    T extends (
      arg: Parameters<T>[number]
    ) => Promise<Auth.UpdateAuthBack | void>
  >(
    callback: T
  ) => (params: Parameters<T>[number]) => Promise<Auth.UpdateAuthBack | void> =
    useCallback(
      callback => async params => {
        const result = await callback(params);
        if (!result) return;
        NotificationSuccess({ message: result.message || "成功" });
        setAuth(result.newUser);
        return result;
      },
      []
    );

  const login = useCallback(
    async auth => await handleRequestSetAuth(Auth.login)(auth),
    [handleRequestSetAuth]
  );

  const register = useCallback(
    async auth => await handleRequestSetAuth(Auth.register)(auth),
    [handleRequestSetAuth]
  );

  const updateUsername = useCallback(
    async params => await handleRequestSetAuth(Auth.updateUsername)(params),
    [handleRequestSetAuth]
  );

  const updatePassword = useCallback(
    async params => await handleRequestSetAuth(Auth.updatePassword)(params),
    [handleRequestSetAuth]
  );

  const verificationMailCode = useCallback(
    async params =>
      await handleRequestSetAuth(Auth.verificationMailCode)(params),
    [handleRequestSetAuth]
  );

  return (
    <MyContext.Provider
      value={{
        auth,
        login,
        register,
        signOut,
        updateUsername,
        updatePassword,
        verificationMailCode,
      }}
      {...props}
    />
  );
};

export const useAuth = () => useContext(MyContext);

export default AuthContext;
